The present invention relates to database systems.
In conventional relational databases, all data are stored in named tables. The tables are described by their features. In other words, the rows of each table contain items of identical type, and the definitions of the columns of the table (i.e., the column names and the data types stored in the column) describe the attributes of each of the instances of the object. By identifying its name, its column names and the data types of the column contents, a table is completely described. Queries to a relational database are formulated in a query language. One such language is SQL (Structure Query Language) which is widely used in commercial relational database systems. The data types offered by SQL can be classified as character arrays (names), numbers, and data types related to date and time. Tables can be modified or combined by several operations of relational algebra such as the application of Boolean operators, projection (i.e. selection of columns) or the Cartesian product.
Relational databases offer several advantages. Database queries are based on a comparison of the table contents. Thus, no pointers are required in relational databases, and all relations are treated uniformly. Further, the tables are independent (they are not related by pointers), so it is easier to maintain dynamic data sets. The tables are easily expandable by simply adding new columns. Also, it is relatively easy to create user-specific views from relational databases.
There are, however, a number of disadvantages associated with relational databases as well. For example, access to data by reference to properties is not optimal in the classical relational data model. This can make such databases cumbersome in many applications.
Another recent technology for database systems is referred to as object oriented database systems. These systems offer more complex data types in order to overcome the restrictions of conventional relational databases. In the context of object oriented database models, an “object” includes both data and the functions (or methods) which can be applied to the object. Each object is a concrete instance of an object class defining the attributes and methods of all its instances. Each instance has its unique identifier by which it can be referred to in the database.
Object oriented databases operate under a number of principles. One such principle is referred to as inheritance. Inheritance means that new object classes can be derived from another class. The new classes inherit the attributes and methods of the other class (the super-class) and offer additional attributes and operations. An instance of the derived class is also an instance of the super-class. Therefore, the relation between a derived class and its super-class is referred to as the “isA” relation.
A second principle related to object oriented databases is referred to as “aggregation.” Aggregation means that composite objects may be constructed as consisting of a set of elementary objects. A “container object” can communicate with the objects contained therein by their methods of the contained objects. The relation between the container object and its components is called a “partOf” relation because a component is a part of the container object.
Yet another principle related to object oriented databases is referred to as encapsulation. According to encapsulation, an application can only communicate with an object through messages. The operations provided by an object define the set of messages which can be understood by the object. No other operations can be applied to the object.
Another principle related to object oriented databases is referred to as polymorphism. Polymorphism means that derived classes may re-define methods of their super-classes.
Objects present a variety of advantages. For example, operations are an important part of objects. Because the implementations of the operations are hidden to an application, objects can be more easily used by application programs. Further, an object class can be provided as an abstract description for a wide variety of actual objects, and new classes can be derived from the base class. Thus, if an application knows the abstract description and using only the methods provided by, the application can still accommodate objects of the derived classes, because the objects in the derived classes inherit these methods. However, object oriented databases are not yet as widely used in commercial products as relational databases.
Yet another database technology attempts to combine the advantages of the wide acceptance of relational databases and the benefits of the object oriented paradigm. This technology is referred to as object-relational database systems. These databases employ a data model that attempts to add object oriented characteristics to tables. All persistent (database) information is still in tables, but some of the tabular entries can have richer data structure. These data structures are referred to as abstract data types (ADTs) . An ADT is a data type that is constructed by combining basic alphanumeric data types. The support for abstract data types presents certain advantages. For example, the methods associated with the new data type can be used to index, store, and retrieve records based on the content of the new data type.
Some conventional object-relational databases support an extended form of SQL, sometimes referred to as ObjectSQL. The extensions are provided to support the object model (e.g., queries involving object attributes). However, these object-relational databases are still relational because the data is stored in tables of rows and columns, and SQL, with some extensions, is the language for data definition, manipulation, and query. Both the target of a query and the result of a query are still tables. The extended SQL language is often still the primary interface to the database. Therefore, there is no direct support of host object languages and their objects. This forces programmers to continue to translate between objects and tables.
Data pertaining to the operation of a business such as types of companies forming an enterprise, orders that the company receives from various customers, and what constitutes an order is hierarchical. As discussed above, relational systems for storing data, on the other hand, are tabular in nature, and consequently, do not directly represent hierarchies. Accordingly, object programming models do not represent the hierarchy of business data very well.
In object-relational database systems, most objects or entities have relationships to other objects, forming a graph of objects with relationships between them. For a particular root object or entity identified in response to a query, the object graph can include the root object (and its metadata and other attributes), as well as child objects (and their metadata and other attributes) and associations between the root object and other objects or entities. Managing when and how parts of the graph are faulted in (i.e., retrieved into random access memory from the long term storage device such as a disc drive, or from another system) and how parts of the graph are trimmed when the graph is serialized are important, primarily for achieving high performance.
For high performance it is desirable to have the set of objects expected to be read when the original object is requested rather than being faulted in. This is hereafter referred to as “eager loading.” It is also desirable to have the unlikely set of objects to be read not to be eager loaded, but instead “faulted on-demand” when the relationship is traversed. “Faulted on demand” can be defined as an entity whose load has been deferred to that point when it is needed. A given relationship for an object instance is either faulted on-demand or eager loaded, but not both.
It is not efficient to return unused (but related) objects when an object is returned. This is especially true if there are no ways to limit the object graph when retrieving the original object. In this situation, potentially the entire data set in the object graph will be read, with the majority of the objects not needed or used. If in response to a query the entire object graph for the root object is read, system resources are not used efficiently because much of the data may not be needed.
Similarly, it is inefficient to return too few needed objects when an object is requested. If in response to the query less than the entire object graph is returned (i.e., the object graph is “trimmed”), efficiency could be lost due to the latency time required to later fault in the additional objects and associations when later needed. Each subsequent fault involves fixed network and latency overhead, in addition to the persistence layer overhead of having to build and execute a new query. Due to these overhead times associated with faulting, it is often more efficient to eagerly load the needed objects with the original request.
If the consumer on each request specifies which relationships to fault and which to eager load, this results in ideal performance since it can be fine-tuned depending on the scenario. However, this is typically not very convenient for the consumer, and sometimes the consumer does not know in advance what the needed objects are.
Another source of inefficiency relates to duplicate objects. Once an object is faulted in it does not have to be faulted in again (for a given graph instance). In addition to being important for performance, it is also important to prevent duplicate objects in a graph or working set. A duplicate object is a copy of the same information and identity as another object. Note, this not the same as having different references to the same object, which is not a concern or problem.
Not remembering previously faulted objects means that duplicate objects are more likely to get into a graph or working set, which leads to problems with concurrency. For example, updating two copies of the same data will most likely cause the first to succeed and the second to fail due to concurrency issues (the data has already changed after the first update occurs). It is best if the second request for the same object returned the previously found instance, so there is only one copy in memory. However, it has conventionally been difficult to achieve this.
Also, for high performance, it is desirable to have specific non-essential objects trimmed from a serialized graph. A serialized graph is a copy of a graph converted into a format (such as text or binary) that can be persisted to disk, cloned, transported across address spaces and machines or other similar purposes. The primary use of a serialized graph is that it can be de-serialized to create a copy of the original graph. Serializing in this way is also used to marshal (prepare for network transmission) by-value (copy the object rather than an reference to it).
Trimming a graph is important for performance because serialization will not need to occur on the trimmed objects, making the resulting serialized graph smaller in memory. Trimming a graph improves de-serialization performance because there are fewer objects to de-serialize.